package com.slofzx.nuoda.evaluation.project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.slofzx.nuoda.common.dto.UserQueryDTO;
import com.slofzx.nuoda.common.exception.ServiceException;
import com.slofzx.nuoda.common.feign.UserClient;
import com.slofzx.nuoda.common.vo.SysUserVO;
import com.slofzx.nuoda.custom.template.service.TemplateService;
import com.slofzx.nuoda.daqing.wechat.enums.AccountTypeEnum;
import com.slofzx.nuoda.daqing.wechat.util.WechatUtil;
import com.slofzx.nuoda.daqing.wechat.vo.WechatResultVO;
import com.slofzx.nuoda.evaluation.param.enums.ParamVersionStateEnum;
import com.slofzx.nuoda.evaluation.param.service.ProjectCostDataService;
import com.slofzx.nuoda.evaluation.project.enums.*;
import com.slofzx.nuoda.evaluation.core.util.StringUtil;
import com.slofzx.nuoda.evaluation.project.compont.exported.ExportExcelContext;
import com.slofzx.nuoda.evaluation.project.compont.exported.PlanExportExcelComponent;
import com.slofzx.nuoda.evaluation.project.compont.imported.ImportExcelContext;
import com.slofzx.nuoda.evaluation.project.compont.imported.PlanImportExcelComponent;
import com.slofzx.nuoda.evaluation.project.dto.BatchCommonDataDTO;
import com.slofzx.nuoda.evaluation.project.dto.ProjectInfoDTO;
import com.slofzx.nuoda.evaluation.project.entity.PlanInfo;
import com.slofzx.nuoda.evaluation.project.dto.PlanInfoDTO;
import com.slofzx.nuoda.evaluation.project.observer.BatchObserver;
import com.slofzx.nuoda.evaluation.project.service.BatchCommonDataService;
import com.slofzx.nuoda.evaluation.project.service.ProjectInfoService;
import com.slofzx.nuoda.evaluation.project.util.BusiUtil;
import com.slofzx.nuoda.evaluation.project.vo.BatchInfoNameVO;
import com.slofzx.nuoda.evaluation.project.vo.PlanInfoVO;
import com.slofzx.nuoda.evaluation.project.dao.PlanInfoDAO;
import com.slofzx.nuoda.evaluation.project.service.PlanInfoService;
import com.slofzx.nuoda.evaluation.project.vo.ProjectInfoVO;
import com.slofzx.nuoda.evaluation.steretype.ExcelExportItemLoader;
import com.slofzx.nuoda.excel.entity.TemplateConfig;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.slofzx.nuoda.common.enums.CommonResultEnum;
import com.slofzx.nuoda.common.enums.ServiceResult;
import com.slofzx.nuoda.common.service.impl.BaseServiceImpl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 评价方案基本信息表服务实现类
 *
 * @author nuoda
 * @since 2024-03-15 09:21:38
 */
@Service("planInfoService")
public class PlanInfoServiceImpl extends BaseServiceImpl implements PlanInfoService {
    @Autowired
    private PlanInfoDAO planInfoDAO;
    @Autowired
    private BatchCommonDataService batchCommonDataService;
    @Autowired
    private PlanImportExcelComponent importExcelComponent;
    @Autowired(required = false)
    protected Set<BatchObserver> batchObservers;
    @Autowired
    private PlanExportExcelComponent exportExcelComponent;
    @Autowired
    private ProjectInfoService projectInfoService;
    @Autowired
    private ExcelExportItemLoader excelExportItemLoader;
    @Autowired
    private TemplateService templateService;
    @Autowired
    private ProjectCostDataService projectCostDataService;

    @Autowired
    private WechatUtil wechatUtil;
    @Autowired
    private UserClient userClient;

    /**
     * 将数据表数据转换为返回数据类型
     *
     * @param entity 数据表对应类
     * @return 实例对象
     */
    protected PlanInfoVO parseEntity2VO(PlanInfo entity) {
        PlanInfoVO vo = new PlanInfoVO();
        vo.setId(entity.getId());
        vo.setName(entity.getName());
        vo.setParamVersion(entity.getParamVersion());
        vo.setModelId(entity.getModelId());
        vo.setState(entity.getState());
        vo.setCostEvaMethod(entity.getCostEvaMethod());
        vo.setCostEvaMethodBool(entity.getCostEvaMethod());
        vo.setProductType(entity.getProductType());
        vo.setYear(entity.getYear());
        vo.setCutoff(entity.isCutoff());
        vo.setBuildOffice(entity.getBuildOffice());
        vo.setAreaMark(entity.getAreaMark());
        vo.setRegion(entity.getRegion());
        vo.setProductProperty(entity.getProductProperty());
        vo.setDisplacementMethod(entity.getDisplacementMethod());
        vo.setCapacityType(entity.getCapacityType());
        vo.setPoolType(entity.getPoolType());
        vo.setEvaluationMethod(entity.getEvaluationMethod());
        vo.setVersionId(entity.getVersionId());
        vo.setCreateUser(entity.getCreateUser());
        vo.setCreateTime(entity.getCreateTime());
        vo.setOilGasField(entity.getOilGasField());
        vo.setOther1(entity.getOther1());
        vo.setOther2(entity.getOther2());
        vo.setPlanType(entity.getPlanType());
        return vo;
    }

    /**
     * 将数据传递类转换为数据表数据类
     *
     * @param dto 数据传递类实例
     * @return 实例对象
     */
    protected PlanInfo parseDTO2Entity(PlanInfoDTO dto) {
        PlanInfo entity = new PlanInfo();
        entity.setId(dto.getId());
        entity.setName(dto.getName());
        entity.setParamVersion(dto.getParamVersion());
        entity.setModelId(dto.getModelId());
        entity.setState(dto.getState());
        entity.setCostEvaMethod(dto.getCostEvaMethod());
        entity.setProductType(dto.getProductType());
        entity.setYear(dto.getYear());
        entity.setCutoff(dto.isCutoff());
        entity.setBuildOffice(dto.getBuildOffice());
        entity.setAreaMark(dto.getAreaMark());
        entity.setRegion(dto.getRegion());
        entity.setProductProperty(dto.getProductProperty());
        entity.setDisplacementMethod(dto.getDisplacementMethod());
        entity.setCapacityType(dto.getCapacityType());
        entity.setPoolType(dto.getPoolType());
        entity.setEvaluationMethod(dto.getEvaluationMethod());
        entity.setVersionId(dto.getVersionId());
        entity.setOilGasField(dto.getOilGasField());
        entity.setOther1(dto.getOther1());
        entity.setOther2(dto.getOther2());
        entity.setPlanType(dto.getPlanType());
        return entity;
    }

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public PlanInfoVO queryById(String id) {
        PlanInfo entity = this.planInfoDAO.selectById(id);
        if (entity == null) {
            return null;
        }
        return parseEntity2VO(entity);
    }

    /**
     * 查询多条数据
     *
     * @param dto 数据传递类实例对象
     * @return 对象列表
     */
    @Override
    public List<PlanInfoVO> queryList(PlanInfoDTO dto) {
        //mybatis-plus 写法示例
        //IPage<PlanInfo> pageResult = this.evaModelDAO.selectPage(new Page<>(dto.getPageNo(),dto.getPageSize()),null);
        //List<PlanInfo> dataList = pageResult.getRecords();
        //dto.setCount((int) pageResult.getTotal());
        final SysUserVO user = getUser();
        if (!BusiUtil.checkPermission(user, PermissionEnum.PLAN_QUERY_ALL)) {
            dto.setCreateUser(user.getId());
        }
        List<PlanInfo> dataList = this.planInfoDAO.queryList(dto);
        if (null == dataList || dataList.isEmpty()) {
            return Collections.emptyList();
        }
        final Set<String> planIdList = dataList.stream().map(PlanInfo::getId).collect(Collectors.toSet());
        final ProjectInfoDTO infoDTO = new ProjectInfoDTO();
        infoDTO.setFilterBatchIdSet(planIdList);
        final List<ProjectInfoVO> infoVOList = projectInfoService.queryVOList(infoDTO);
        Map<String, Double> doubleMap = null;
        if (CollectionUtils.isNotEmpty(infoVOList)) {
            doubleMap = infoVOList.stream().filter(ProjectInfoVO::isUltimate).collect(Collectors.toMap(ProjectInfoVO::getBatchId, ProjectInfoVO::getTotalInvest,(v1,v2)->v1));
        }
        List<PlanInfoVO> resultList = new ArrayList(dataList.size());
        for (PlanInfo item : dataList) {
            PlanInfoVO vo = parseEntity2VO(item);
            if (MapUtils.isNotEmpty(doubleMap)) {
                vo.setTotalInvest(doubleMap.get(item.getId()));
            }
            resultList.add(vo);
        }
        // TODO 添加其他关联查询数据
        return resultList;
    }

    /**
     * 新增数据
     *
     * @param dto 实例对象
     * @return 操作结果
     */
    @Override
    public ServiceResult create(PlanInfoDTO dto) {
        // TODO 添加校验逻辑
        BusiUtil.checkNotNull(dto.getName(), "方案名称不能为空");
        BusiUtil.checkNotNull(dto.getParamVersion(), "公共参数不能为空");
        BusiUtil.checkNotNull(dto.getModelId(), "评价模型不能为空");
        checkDuplicateName(dto.getName(), null);
        dto.setCutoff(false);
        PlanInfo entity = parseDTO2Entity(dto);
        entity.setState(ParamVersionStateEnum.INIT.getId());
        preInsert(entity);
        this.planInfoDAO.insert(entity);
        return CommonResultEnum.SUCCESS;
    }
    /**
     * 检查任务名称是否重复
     *
     * @param name
     */
    private void checkDuplicateName(String name, String id) {
        LambdaQueryWrapper<PlanInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(PlanInfo::getName, name);
        if (StringUtils.isNotEmpty(id)) {
            queryWrapper.ne(PlanInfo::getId, id);
        }
        final Long count = planInfoDAO.selectCount(queryWrapper);
        if (count > 0) {
            throw new ServiceException("方案名称重复，请修改后提交", null, null);
        }
    }

    /**
     * 修改数据
     *
     * @param dto 实例对象
     * @return 操作结果
     */
    @Override
    public ServiceResult update(PlanInfoDTO dto) {
        String id = dto.getId();
        if (StringUtils.isEmpty(id)) {
            return CommonResultEnum.NOT_EXIST;
        }
        if (StringUtils.isNotEmpty(dto.getName())) {
            checkDuplicateName(dto.getName(), id);
        }
        PlanInfo entity = this.planInfoDAO.selectById(id);
        if (entity == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        BatchStateEnum stateEnum = BatchStateEnum.getEnum(entity.getState());
        if(stateEnum == BatchStateEnum.PUBLISH) {
            return CommonResultEnum.PERMISSION_DENY;
        }
        // 当方案状态未锁定时，方案版本或者组织机构变更同步变化项目成本数据
        if (BatchStateEnum.INIT.getId().equals(entity.getState()) &&
                ((StringUtils.isNotEmpty(dto.getParamVersion()) && !dto.getParamVersion().equals(entity.getParamVersion())) ||
                (StringUtils.isNotEmpty(dto.getBuildOffice()) && !dto.getBuildOffice().equals(entity.getBuildOffice())))) {
            final ProjectInfoDTO infoDTO = new ProjectInfoDTO();
            infoDTO.setBatchId(entity.getId());
            final List<ProjectInfoVO> infoVOList = projectInfoService.queryList(infoDTO);
            for (ProjectInfoVO vo : infoVOList) {
                vo.setBuildOffice(dto.getBuildOffice());
            }
            // final Set<String> projectIdList = infoVOList.stream().map(ProjectInfoVO::getId).collect(Collectors.toSet());
            projectCostDataService.updateByParamVersion(dto.getParamVersion() ,infoVOList);
        }
        entity.setName(dto.getName());
        entity.setParamVersion(dto.getParamVersion());
        entity.setModelId(dto.getModelId());
        //更新不能更新状态，使用锁定、解锁逻辑更新状态
//        entity.setState(dto.getState());
        entity.setCostEvaMethod(dto.getCostEvaMethod());
        entity.setProductType(dto.getProductType());
        entity.setYear(dto.getYear());
        entity.setCutoff(dto.isCutoff());
        entity.setBuildOffice(dto.getBuildOffice());
        entity.setAreaMark(dto.getAreaMark());
        entity.setRegion(dto.getRegion());
        entity.setProductProperty(dto.getProductProperty());
        entity.setDisplacementMethod(dto.getDisplacementMethod());
        entity.setCapacityType(dto.getCapacityType());
        entity.setPoolType(dto.getPoolType());
        entity.setEvaluationMethod(dto.getEvaluationMethod());
        entity.setVersionId(dto.getVersionId());
        entity.setOilGasField(dto.getOilGasField());
        entity.setOther1(dto.getOther1());
        entity.setOther2(dto.getOther2());
        entity.setPlanType(dto.getPlanType());
        this.planInfoDAO.updateById(entity);
        return CommonResultEnum.SUCCESS;
    }
    /**
     * 锁定数据
     *
     * @param dto 实例对象
     * @return 操作结果
     */
    @Override
    public ServiceResult lock(PlanInfoDTO dto) {
        String id = dto.getId();
        if (StringUtils.isEmpty(id)) {
            return CommonResultEnum.NOT_EXIST;
        }
        PlanInfo entity = this.planInfoDAO.selectById(id);
        if (entity == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        SysUserVO userVO = getUser();
        if (userVO == null || !userVO.getId().equals(entity.getCreateUser())) {
            //只允许本人进行锁定
            return CommonResultEnum.PERMISSION_DENY;
        }


        entity.setState(BatchStateEnum.PUBLISH.getId());
        this.planInfoDAO.updateById(entity);
        List<SysUserVO> userList = userClient.queryUserInfoListByPermission(PermissionEnum.PLAN_UNLOCK.getId()).getData();
        if(userList!=null) {
            //锁定后发送即时通消息,有解锁权限的用户会收到消息
            for(SysUserVO user:userList) {
                try {
                    WechatResultVO resultVO = wechatUtil.sendMessage(AccountTypeEnum.USERID, user.getId(), entity.getName() + "项目经济评价工作已完成并提交，请审核！", "evaluation", "evaluation");
                    logger.debug("方案锁定发送即时通 {} 消息结果:{}",user.getId(),resultVO);
                }catch (Exception e) {
                logger.error("方案锁定发送即时通消息失败:{}",user.getId(),e);
            }
            }
        }
        return CommonResultEnum.SUCCESS;
    }
    /**
     * 解锁数据
     *
     * @param dto 实例对象
     * @return 操作结果
     */
    @Override
    public ServiceResult unlock(PlanInfoDTO dto) {
        String id = dto.getId();
        if (StringUtils.isEmpty(id)) {
            return CommonResultEnum.NOT_EXIST;
        }
        PlanInfo entity = this.planInfoDAO.selectById(id);
        if (entity == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        entity.setState(BatchStateEnum.INIT.getId());
        this.planInfoDAO.updateById(entity);
        try {
            //解锁后给方案创建人发送即时通消息
            WechatResultVO resultVO = wechatUtil.sendMessage(AccountTypeEnum.USERID, entity.getCreateUser(), entity.getName() + "项目已退回，请按要求调整！！", "evaluation", "evaluation");
            logger.debug("方案解锁发送即时通 {} 消息结果:{}",entity.getCreateUser(),resultVO);
        }catch (Exception e) {
            logger.error("解锁后给方案创建人发送即时通消息失败:{}",entity.getCreateUser(),e);
        }

        return CommonResultEnum.SUCCESS;
    }

    /**
     * 通过主键删除数据
     *
     * @param id 主键
     * @return 操作结果
     */
    @Override
    public ServiceResult deleteById(String id) {
        // TODO 添加校验逻辑，如数据权限
        if (batchObservers != null) {
            for (BatchObserver item : batchObservers) {
                ServiceResult result = item.handleBatchDelete(id);
                if (CommonResultEnum.SUCCESS != result) {
                    logger.error("项目监听者 {} 处理任务删除事件失败: {}", item.getName(), result);
                }
            }
        }

        this.planInfoDAO.deleteById(id);
        return CommonResultEnum.SUCCESS;
    }

    @Override
    public List<BatchInfoNameVO> queryNameList() {
        final PlanInfoDTO dto = new PlanInfoDTO();
        final List<PlanInfo> batchInfos = planInfoDAO.queryList(dto);
        if (CollectionUtils.isEmpty(batchInfos)) {
            return Collections.EMPTY_LIST;
        }

        return batchInfos.stream().map(m -> {
            final BatchInfoNameVO batchInfoNameVO = new BatchInfoNameVO();
            batchInfoNameVO.setId(m.getId());
            batchInfoNameVO.setValueKey(m.getId());
            batchInfoNameVO.setName(m.getName());
            return batchInfoNameVO;
        }).collect(Collectors.toList());
    }
    @Override
    @Transactional
    public ServiceResult editCommonData(PlanInfoDTO dto) {
        String id = dto.getId();
        String name = dto.getName();
        String paramVersion = dto.getParamVersion();
        String modelId = dto.getModelId();
        String costEvaMethod = dto.getCostEvaMethod();
        if (StringUtils.isBlank(id) || StringUtils.isBlank(name) || StringUtils.isBlank(paramVersion) ||
                StringUtils.isBlank(modelId) || StringUtils.isBlank(costEvaMethod)) {
            return CommonResultEnum.VALUE_LOOSE;
        }
        if (CostEvaMethodEnum.getEnum(costEvaMethod) == null) {
            return CommonResultEnum.NOT_EXIST;
        }

        PlanInfo planInfo = planInfoDAO.selectById(id);
        if (planInfo == null) {
            return CommonResultEnum.NOT_EXIST;
        }
//        LambdaUpdateWrapper<PlanInfo> updateWrapper = new LambdaUpdateWrapper();
//        updateWrapper.eq(PlanInfo::getId, id);
//        updateWrapper.set(PlanInfo::getName, name);
//        updateWrapper.set(PlanInfo::getParamVersion, paramVersion);
//        updateWrapper.set(PlanInfo::getModelId, modelId);
//        updateWrapper.set(PlanInfo::getCostEvaMethod, costEvaMethod);
//        updateWrapper.set(PlanInfo::isCutoff,dto.isCutoff());
//        planInfoDAO.update(new PlanInfo(), updateWrapper);
        planInfo.setName(dto.getName());
        planInfo.setModelId(dto.getModelId());
        planInfo.setCostEvaMethod(dto.getCostEvaMethod());
        planInfo.setCutoff(dto.isCutoff());
        planInfo.setParamVersion(dto.getParamVersion());
        planInfoDAO.updateById(planInfo);
        List<BatchCommonDataDTO> batchCommonDTOList = dto.getBatchCommonDTOList();
        if (batchCommonDTOList != null && !batchCommonDTOList.isEmpty()) {
            batchCommonDataService.batchInsertData(id, batchCommonDTOList);
        }
        return CommonResultEnum.SUCCESS;
    }

    /**
     * 导出项目数据
     * @param projectInfoDTO
     */
    @Override
    public ResponseEntity<byte[]> exportExcel(HttpServletRequest request, ProjectInfoDTO projectInfoDTO) {
        final PlanInfo planInfo = planInfoDAO.selectById(projectInfoDTO.getBatchId());
        String excelName = planInfo.getName() + "导出数据.xlsx" ;
        TemplateConfig config = templateService.getTemplateConfig(projectInfoDTO.getTemplateId());
        final ExportExcelContext exportExcelContext = new ExportExcelContext();
        exportExcelContext.setId(planInfo.getId());
        exportExcelContext.setCostEvaMethod(planInfo.getCostEvaMethod());
        exportExcelContext.setProductType(BusiUtil.getEnumById(planInfo.getProductType(),ProductTypeEnum.class));
        exportExcelContext.setConfig(config);
        exportExcelContext.setExcelExportItemList(excelExportItemLoader.getDataList());
        exportExcelContext.setProjectInfoDTO(projectInfoDTO);
        try {
            final byte[] bytes = exportExcelComponent.exportExcel(exportExcelContext);
            HttpHeaders headers = new HttpHeaders();
            headers.add("content-disposition", "attachment; filename=" + StringUtil.convertDownloadFileName(excelName,request.getHeader("User-Agent")));
            HttpStatus statusCode = HttpStatus.OK;
            ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(bytes, headers, statusCode);
            return entity;
        } catch (Exception e) {
            logger.error("导出失败batchId {}", planInfo.getId(),e);
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServiceResult importExcel(String fileId, String batchId) {
        if (StringUtils.isBlank(fileId)) {
            return CommonResultEnum.VALUE_LOOSE;
        }

        final PlanInfo planInfo = planInfoDAO.selectById(batchId);
        if (planInfo == null) {
            return CommonResultEnum.NOT_EXIST;
        }
        final ImportExcelContext importExcelContext = new ImportExcelContext();
        importExcelContext.setId(batchId);
        importExcelContext.setBatchQuality(BatchQualityEnum.PLAN);
        importExcelContext.setProductType(BusiUtil.getEnumById(planInfo.getProductType(), ProductTypeEnum.class));
        importExcelContext.setFileId(fileId);
        ServiceResult serviceResult = importExcelComponent.importExcel(importExcelContext);
        return serviceResult;


    }
}
